嗨 大家好 我是一路爬坡的阿肥
最近天氣開始有點涼爽,騎車去上班瀏海也不分岔了~
上班族小確幸++
今日文章適合搭配範例專案的packages/day06-creational-factory-method.code
一起觀看,歡迎把專案clone下來喔
阿肥的範例專案以Mono-Repo的方式管理,在packages
底下的所有目錄都會視為獨立的專案,所以我們可以看到會有package.json
設定相關資訊,source code的部分主要會放在src
的目錄中。
以今天的例子來說,基本上會有這些檔案:
declaration.ts
: 類別、屬性、資料結構等相關的定義factory.ts
: 設計模式實作的類別index.tsx
:React元件的主程式Vermicelli.story.tsx
:執行Storybook後,呈現UI flow 的story檔。Storybook是最近幾年相當熱門的前端開發工具,對於元件的手動測試幫助相當大,阿肥之後會再抽出一天跟大家介紹喔。Vermicelli.test.tsx
:元件的測試檔,本專案用Facebook開發的Jest作為測試框架,之後也會進行分享。乍聽之下覺得是廢話,不過其實是有意義的!就像小時候玩黏土一樣,你會先知道要先做手、腳、頭、身軀,最後再接起來形成人偶。我們要先定義麵線的口味和顏色、客人填的菜單有哪些選項,以及最後給客人的產品。
在接下來的範例中,阿肥會把這些定義包在一個 namespace 中,只要其他檔案 reference 到這個檔案,就可以參考到裡面的定義。
namespace Declaration {
// 麵線顏色的種類
export type T_Color = "white" | "red";
// 麵線的口味種類,目前有大腸跟蚵仔兩種選項
export type T_Flavor = "intestine" | "oyster";
// 客人的菜單內容
export interface I_Order {
flavor: T_Flavor;
color: T_Color;
spoons: number;
}
// 最後給客人的麵線組成,繼承了I_Order
export interface I_Vermicelli extends I_Order {
trayed: boolean;
content: string[];
}
}
首先,我們先把定義檔參考進來:/// <reference path="declaration.ts" />
接著,我們實作一個抽象類別BaseVermicelli
來為流程做出一個介面,讓做白麵線的WhiteVermicelli
跟RedVermicelli
可以繼承他實作各自的流程。
最後,我們實作類別FrontStaff
與VermicelliFactory
。因為主程式中,我們只會用到 VermicelliFactory
來取得麵線,其他的類別與邏輯都會封裝在裡面,所以我們只要匯出這個即可。
// 用 abstract 的關鍵字宣告這個是抽象類別
abstract class BaseVermicelli {
//...
}
//繼承 BaseVermicelli
class WhiteVermicelli extends BaseVermicelli {
//...
}
//繼承 BaseVermicelli
class WhiteVermicelli extends BaseVermicelli {
//...
}
class FrontStaff {
//...
}
// 用 export 匯出 VermicelliFactory 給外部使用
export class VermicelliFactory {
//...
}
跟factory.ts
一樣,我們先把定義先參考進來:/// <reference path="declaration.ts" />
接著是React起手式,先宣告一個元件,先簡單return一個div。export const FatVermicelli: React.FC => <div></div>
阿肥這邊把FatVermicelli
用React.FC
定義為這是個 Functional Component (FC)。簡單來說,不同於過去繼承React元件類別的方式,而是用最單純的方式 - function 建立元件,不僅可以更方便為元件的邏輯進行測試,在React 16.8 之後正式推出的 React Hooks,更是讓FC可以實現state的管理、life cycle的事件處理等等。所以接下來的範例,阿肥都會盡量用FC實作,讓大家順便熟悉喔。
客人菜單的部分我們用表單元件實作,然後用React.useState
管理各欄位值的變化,就像這樣:
// Declaration.I_Order 定義 order state,並給定預設值
const [order, setOrder] = React.useState<Declaration.I_Order>({
color: "red",
flavor: "intestine",
spoons: 1
});
// 用 setOrder 更新欄位值
const handleFieldChange = ({ target }) => {
setOrder(order => ({
...order,
[target.name]: target.value
}));
};
// value傳入 order.flavor 控制欄位值,並在 onChange 事件傳入 handleFieldChange 執行 setOrder
<select
value={order.flavor}
id="flavor"
name="flavor"
onChange={handleFieldChange}
>
<option value="intestine">大腸</option>
<option value="oyster">蚵仔</option>
</select>
當送出表單後,我們用表單的 onSubmit
事件來觸發製作麵線的方法,並且定義props裡有個 onSubmit
,將做好的麵線傳給外部。
interface I_Props_FatVermicelli {
// 當麵線完成後,再交給外面的人
onSubmit: (v: Declaration.I_Vermicelli) => void;
}
// 宣告FC的 props 型別為 I_Props_FatVermicelli
export const FatVermicelli: React.FC<I_Props_FatVermicelli> = ({
onSubmit
}) => {
// 建立一個麵線工廠的實體
const vermicelliFactory = new VermicelliFactory();
// 執行製作麵線的流程,並回傳給 props 的 onSubmit
const handleSubmitOrder = e => {
e.preventDefault();
vermicelliFactory.receiveOrder(order);
let _vermicelli = vermicelliFactory.maker.vermicelli;
if (onSubmit) onSubmit(_vermicelli);
};
//...
return (
// 在 onChange 事件傳入 handleSubmitOrder 執行
<form onSubmit={handleSubmitOrder}>
)
}
執行yarn story
後,開啟http://localhost:6006
,然後切到FatVermicelli
,就可以看到畫面了。
從昨天工廠方法的模式篇,到今天的實作篇,大致是之後介紹每種模式的步驟:先讓大家從生活化的情境了解設計模式的理論,再藉由實作來加深對設計模式的理解。如果有興趣的話,可以訂閱阿肥這系列的文章,這樣阿肥會更有動力完賽喔!